﻿using AnesSystem.Models;
using AnesSystem.Services;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MongoDB.Bson;
using Newtonsoft.Json;
using System.Reflection;
using MongoDB.Bson.Serialization;
using AnesSystem;

namespace AnesSystem
{
    public class OffLineManager : BaseManager<OffLineManager>
    {

        #region Variables

        private Dictionary<string, OffLineModel> objectList;
        private string locaPath = "OffLine\\";
        #endregion

        #region Constructor

        public OffLineManager()
        {
            objectList = new Dictionary<string, OffLineModel>();
        }

        #endregion

        #region Properties

        public Dictionary<string, OffLineModel> ObjectList
        {
            get
            {
                return objectList;
            }

            set
            {
                objectList = value;
            }
        }

        #endregion

        #region 依赖属性

        #endregion

        #region Event

        #endregion

        #region Private Methods

        #endregion

        #region Public/Protected Methods

        public bool SaveFile(string className, object obj)
        {
            try
            {
                bool isAdd = false;
                OffLineModel model;
                if (ObjectList.ContainsKey(className))
                {
                    model = ObjectList[className];
                    model.OffLineObject = obj.ToJson();
                }
                else
                {
                    model = new OffLineModel();
                    model.IsUploaded = false;
                    model.ClassName = className;
                    isAdd = true;
                }
                string json = model.ToJson();
                if (!Directory.Exists(locaPath)) Directory.CreateDirectory(locaPath);
                File.WriteAllText(locaPath + className + ".txt", json);
                if (isAdd) ObjectList.Add(className, model);
                return true;
            }
            catch (Exception ex)
            {
                Common.LogHelper.Error(this.GetType(), ex);
                return false;
            }
        }

        public void DeleteFile(string className)
        {
            try
            {
                if (ObjectList.ContainsKey(className))
                {
                    ObjectList.Remove(className);
                    File.Delete(locaPath + className + ".txt");
                }
            }
            catch (Exception ex)
            {
                Common.LogHelper.Error(this.GetType(), ex);
            }
        }

        public bool HasOffLineFiles()
        {
            int count = 0;
            ObjectList.Clear();
            string[] files = Directory.GetFiles(locaPath);
            if (files.Count() == 0) return false;
            foreach (var item in files)
            {
                string json = File.ReadAllText(item);
                try
                {
                    OffLineModel model = BsonSerializer.Deserialize<OffLineModel>(json);
                    ObjectList.Add(model.ClassName, model);
                    count++;
                }
                catch (Exception ex)
                {
                    Common.LogHelper.Error(this.GetType(), ex);
                }
            }
            if (count != 0) return true;
            else return false;
        }

        public bool UpLoadInfos()
        {
            int count = objectList.Count;
            for (int i = count-1; i >= 0; i--)
            {
                OffLineModel item = objectList.Values.ToList()[i];
                if (!item.IsUploaded)
                {
                    switch (item.ClassName)
                    {
                        case "patientLink":
                            PatientLinkService.Instance.Update(BsonSerializer.Deserialize<PatientLinkModel>(item.OffLineObject));
                            break;
                    }
                }
            }
            return true;
        }
        private static void FillData(BsonElement c, object tObj, IEnumerable<FieldInfo> tPros)
        {
            try
            {
                foreach (var tPro in tPros)
                {
                    if (tPro != null)
                    {
                        if (tPro.FieldType.FullName.Contains("AnesSystem.Models") && !c.Value.IsBsonArray && !c.Name.ToLower().Equals("tables"))
                        {
                            if (tPro.Name.ToLower().Equals(c.Name.ToLower()))
                            {
                                object obj = Activator.CreateInstance(tPro.FieldType);
                                if (c.Value.IsBsonDocument)
                                {
                                    foreach (var e in c.Value.ToBsonDocument().Elements)
                                    {
                                        try
                                        {
                                            if (e.Value.IsBsonNull) continue;
                                            FillData(e, obj, obj.GetType().GetRuntimeFields());
                                        }
                                        catch (Exception ex)
                                        {
                                            Common.LogHelper.Error("BaseService", ex);
                                        }
                                    }
                                }

                                tPro.SetValue(tObj, obj);
                                break;
                            }
                        }
                        else if (c.Name.ToLower().Equals(tPro.Name.ToLower()))
                        {
                            if (c.Name.ToLower().Contains("tables"))
                            {
                                BsonValue root = c.Value;
                                Dictionary<string, CommonTableModel> dic = new Dictionary<string, CommonTableModel>();
                                IEnumerable<BsonElement> elements = root.ToBsonDocument().Elements;
                                foreach (var ele in elements)
                                {
                                    if (ele.Value.ToBsonDocument().GetValue("XmlContent").IsBsonNull) continue;
                                    CommonTableModel table = new CommonTableModel()
                                    {
                                        TableName = ele.Name,
                                        XmlContent = ele.Value.ToBsonDocument().GetValue("XmlContent").AsString
                                    };
                                    dic.Add(ele.Name, table);
                                    continue;

                                    string name = ele.Name;
                                    IEnumerable<BsonElement> itemElements = ele.Value.ToBsonDocument().Elements;
                                    List<ClassHelper.CustPropertyInfo> proList = new List<ClassHelper.CustPropertyInfo>();
                                    Dictionary<string, string> values = new Dictionary<string, string>();
                                    foreach (var iEle in itemElements)
                                    {
                                        ClassHelper.CustPropertyInfo pro = new ClassHelper.CustPropertyInfo("System.String", iEle.Name);
                                        proList.Add(pro);
                                        values.Add(iEle.Name, iEle.Value.IsBsonNull ? "" : iEle.Value.AsString);
                                    }
                                    CommonTableModel tableModel = ClassHelper.CreateInstance(name, proList, "AnesSystem.Models.CommonTableModel") as CommonTableModel;
                                    foreach (var pro in proList)
                                    {
                                        tableModel.GetType().GetProperty(pro.PropertyName).SetValue(tableModel, values[pro.PropertyName]);
                                    }
                                    dic.Add(name, tableModel);
                                }
                                if (dic.Count != 0)
                                    tPro.SetValue(tObj, dic);
                                break;
                            }
                            if (c.Value.IsBoolean)
                            {
                                tPro.SetValue(tObj, c.Value.AsBoolean);
                                break;
                            }
                            else if (c.Value.IsBsonDateTime)
                            {
                                if (c.Value.ToUniversalTime().Equals(new DateTime()))
                                {
                                    tPro.SetValue(tObj, c.Value.AsDateTime);
                                }
                                else
                                {
                                    tPro.SetValue(tObj, c.Value.ToLocalTime());
                                }
                                //tPro.SetValue(tObj, c.Value.AsDateTime);
                                break;
                            }
                            else if (c.Value.IsBsonNull)
                            {
                                //tPro.SetValue(tObj, c.Value.asn);
                                break;
                            }
                            else if (c.Value.IsInt32)
                            {
                                tPro.SetValue(tObj, c.Value.AsInt32);
                                break;
                            }
                            else if (c.Value.IsObjectId)
                            {
                                tPro.SetValue(tObj, c.Value.AsObjectId);
                                break;
                            }
                            else if (c.Value.IsString)
                            {
                                tPro.SetValue(tObj, c.Value.AsString);
                                break;
                            }
                            else if (c.Value.IsBsonArray)
                            {
                                BsonArray arr = c.Value.AsBsonArray;
                                if (arr.Count != 0)
                                {
                                    //ArrayList[] entityList = new object[arr.Count];
                                    BindingFlags flag = BindingFlags.Instance | BindingFlags.Public;
                                    MethodInfo methodInfo = tPro.FieldType.GetMethod("Add", flag);
                                    object arrObjs = Activator.CreateInstance(tPro.FieldType);
                                    int i = 0;
                                    foreach (var o in arr)
                                    {
                                        object arrObj = Activator.CreateInstance(Type.GetType(tPro.FieldType.GetGenericArguments()[0].FullName));
                                        foreach (var e in o.AsBsonDocument)
                                        {
                                            FillData(e, arrObj, arrObj.GetType().GetRuntimeFields());
                                        }
                                        methodInfo.Invoke(arrObjs, new object[] { arrObj });//相当于List<T>调用Add方法
                                    }
                                    tPro.SetValue(tObj, arrObjs);
                                }
                                break;
                            }
                        }
                    }
                }

            }
            catch (Exception ex)
            {
                Common.LogHelper.Error("BaseService", ex);
            }
        }

        #endregion

        #region EventHandlers

        #endregion

    }
    
    public class OffLineModel
    {
        private bool isUploaded = false;
        private string offLineObject;
        private string className = "";

        public OffLineModel(bool isUploaded, string obj, string className)
        {
            this.isUploaded = isUploaded;
            this.ClassName = className;
            this.offLineObject = obj;
        }
        public OffLineModel()
        {
            
        }

        public bool IsUploaded
        {
            get
            {
                return isUploaded;
            }

            set
            {
                isUploaded = value;
            }
        }

        public string OffLineObject
        {
            get
            {
                return offLineObject;
            }

            set
            {
                offLineObject = value;
            }
        }

        public string ClassName
        {
            get
            {
                return className;
            }

            set
            {
                className = value;
            }
        }
    }
}
